#include "cli.h"
#include "log.h"

typedef struct cli_cmd
{
    char name[CLI_ARGS_LEN];
    cli_cb_t cb;
}cli_cmd_t;

//Input buffer
static char    g_cli_char_buf[CLI_CHAR_BUF_SIZE] = {0};
static int32_t g_cli_char_cnt = 0;
//Command registered
static int32_t   g_cli_cmd_cnt = 0;
static cli_cmd_t g_cli_cmd_buf[CLI_CMD_NUM_MAX];

static int32_t cli_parse_cmd(char *args[])
{
    char c;
    char *start = 0;
    char *end   = 0;
    char *p     = 0;
    int32_t pos  = 0;
    int32_t argc = 0;
    int32_t buf_cnt = g_cli_char_cnt;
    
    //Find the start/end position of command string
    while(pos < buf_cnt)
    {
        c = g_cli_char_buf[pos];
        
        if(!start)
        {
        //Find the start char, skip '\r' and '\n'
            if(c != '\r' && c != '\n')
                start = g_cli_char_buf + pos;
        }else
        {
        //already found start char, find end char and change it to '\0' to mark a string end.
            if(c == '\r' || c == '\n')
            {
                end = g_cli_char_buf + pos;
                g_cli_char_buf[pos] = '\0'; 
            }
        }  
        pos++;
    }
  
    //Split command string
    if(start && end)
    {
        
        //dlim is ' ' or '\t'
        p = strtok(start, " \t");
        while(p != NULL)
        {
            args[argc++] = p;
            if(argc >= CLI_ARGS_NUM)
            {
                argc = 0;
                LOGS("The number of args is limited to %d!\r\n", CLI_ARGS_NUM);
                goto EXIT;
            }
            
            p = strtok(NULL, " \t");
        }
    }
     
EXIT:
    //Process one command at one time.
    //Anyway clear char buf in the end.
    g_cli_char_cnt = 0;
    return argc;
}

static void cli_exec_cmd(int32_t argc, char *args[])
{
    int32_t i;
    cli_cmd_t *cmd;
    char *name = args[0];
    
    if(argc <= 0)
        return;
    
    for(i=0; i<g_cli_cmd_cnt; i++)
    {
        cmd = &g_cli_cmd_buf[i];
        if(!strcmp(name, cmd->name) && cmd->cb)
        {
            cmd->cb(argc, args);
            return;
        }
    }
    
    LOGS("Unknown Command\r\n");
}

static void cli_process_cmd(void)
{
    int32_t argc = 0;
    char *args[CLI_ARGS_NUM];
    
    argc = cli_parse_cmd(args);
    cli_exec_cmd(argc, args);
}

void cli_init(void)
{
    g_cli_char_cnt = 0;
    g_cli_cmd_cnt  = 0;
    
    LOGS_EN(1);
}

void cli_input_char(char c)
{   
    //If buf if full, drop all data.
    if(g_cli_char_cnt >= CLI_CHAR_BUF_SIZE)
    {
        g_cli_char_cnt = 0;
    }else
    {
        g_cli_char_buf[g_cli_char_cnt++] = c;
        
        //Echo input.
        LOGS("%c", c);
        //If input an enter, start processing the command.
        if((c == '\r' || c == '\n'))
        {
            LOGS("\r\n");
            cli_process_cmd();
            LOGS("%s", CLI_PROMPT);
        }
    }
}

int32_t cli_register_cmd(const char *name, cli_cb_t cb)
{
    cli_cmd_t *cmd;
    
    if(strlen(name) > (CLI_ARGS_LEN-1) || cb == 0)
        return -1;
    
    if(g_cli_cmd_cnt >= CLI_CMD_NUM_MAX)
        return -1;
    
    cmd = &g_cli_cmd_buf[g_cli_cmd_cnt++];
    cmd->cb   = cb;
    strcpy(cmd->name, name);
    
    return 0;
}

#include "loopsys.h"
#include "console.h"

static void cli_task_func(loop_task_t *self, uint32_t events)
{
    char c;
    int32_t len;

    len = console_recv((uint8_t *)&c, 1);
    while(len)
    {
        cli_input_char(c);
        len = console_recv((uint8_t *)&c, 1);
    }

    loopsys_event_clear_all(self);
}

void cli_task_init(void)
{
    loop_task_t *task;
    
    cli_init();
    task = loopsys_task_add(LOOPSYS_TASK_NUM - 1, cli_task_func);
    if(task)
    {
        loopsys_task_timer_enable(task, LOOPSYS_TIMER_FOREVER, 50);
        loopsys_task_timer_start(task);
    }
}
